﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Hexagonal;
using System.Threading;
using System.Runtime.Serialization;
using System.Runtime.InteropServices;


namespace GeoSwarm_Editor
{
    public partial class Main : Form
    {
        private Hexagonal.Board board;
        private Hexagonal.GraphicsEngine graphicsEngine;
        private List<Hex> spawnHexes;
        private int lastHexID = 0;
        private LevelData levelData;
        private AboutBox about = new AboutBox();
        private bool hexesChanged = false;
        private string saveFilename = string.Empty;
        private string defaultTitle;
        private bool restarting = false;
        private string thisVersion = "beta-02";
        private Thread grid;
        private Hex lastActiveLocked;
        private bool forceTermination = false;

        private void ValidateVersionNumber()
        {
            UpdateChecker uc = new UpdateChecker(thisVersion);
            if (uc.UpdateAvailable())
            {
                DisableHide();
                MessageBox.Show("There is a newer version of GeoSwarm Editor available. You must update to continue using GeoSwarm Editor.", "Update Required");
                ForceTerminate();
            }
        }

        private delegate void DisableHideCallback();

        private void DisableHide()
        {
            if (this.InvokeRequired)
            {
                DisableHideCallback d = new DisableHideCallback(DisableHide);
                this.Invoke(d);
            }
            else
            {
                if (graphicsEngine != null)
                {
                    graphicsEngine = null;
                }

                if (board != null)
                {
                    board = null;
                }
                this.Enabled = false;
                this.Hide();
            }
        }

        private delegate void ForceTerminateCallback();

        private void ForceTerminate()
        {
            if (this.InvokeRequired)
            {
                ForceTerminateCallback d = new ForceTerminateCallback(ForceTerminate);
                this.Invoke(d);
            }
            else
            {
                forceTermination = true;
                Application.Exit();
                Environment.Exit(0);
                throw new Exception("Force termination occurred");
            }
        }  
  
        public Main()
        {
            //Thread versionCheck = new Thread(new ThreadStart(ValidateVersionNumber));
            //versionCheck.Start();
            InitializeComponent();
            grid = new Thread(new ThreadStart(CreateHexGrid));
            grid.Start();
            statusText.Text = "Ready";
            statusText.ForeColor = SystemColors.ControlText;
            levelData = new LevelData();
            spawnHexes = levelData.SpawnHexes;
            control_HexTool.SelectedIndex = 1;
            refreshTimer.Enabled = true;
            defaultTitle = this.Text;
        }

        private void CreateHexGrid()
        {
            Hexagonal.HexOrientation orientation = HexOrientation.Flat;
            orientation = Hexagonal.HexOrientation.Flat;

            Board board = new Board(14, 16, 16, orientation, Color.FromArgb(180, 0, 20, 5));
            board.BoardState.BackgroundColor = Color.FromArgb(0,63,60);
            board.BoardState.GridPenWidth = 1;
            //board.BoardState.GridColor = Color.FromArgb(41, 63, 60);
            board.BoardState.GridColor = Color.FromArgb(0, 255, 255, 255);
            //board.BoardState.ActiveHexBorderColor = Color.FromArgb(47,219,71);
            board.BoardState.ActiveHexBorderColor = Color.White;
            board.BoardState.ActiveHexBorderWidth = 2;

            this.board = board;
            this.graphicsEngine = new GraphicsEngine(board, 20, 39);
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!forceTermination)
            {
                DialogResult result;
                if (this.restarting)
                    result = MessageBox.Show("Do you want to save changes to \"" + levelData.LevelName + "\"?",
                                "Save Changes", MessageBoxButtons.YesNo);
                else
                    result = MessageBox.Show("Do you want to save changes to \"" + levelData.LevelName + "\"?",
                                "Save Changes", MessageBoxButtons.YesNoCancel);
                if (result == DialogResult.Yes)
                {
                    if (saveFilename == string.Empty)
                        if (SaveDialog.ShowDialog() == DialogResult.Cancel)
                            e.Cancel = true;
                        else
                            SaveLevel(saveFilename);
                }
                else if (result == DialogResult.Cancel)
                {
                    e.Cancel = true;
                }

                if (e.Cancel == true && this.restarting)
                    e.Cancel = false;

                if (e.Cancel != true)
                {
                    if (graphicsEngine != null)
                    {
                        graphicsEngine = null;
                    }

                    if (board != null)
                    {
                        board = null;
                    }
                }
            }
        }

        private void Main_Paint(object sender, PaintEventArgs e)
        {
            //Draw the graphics/GUI

            foreach (Control c in this.Controls)
            {
                c.Refresh();
            }

            if (graphicsEngine != null)
            {
                graphicsEngine.Draw(e.Graphics);
            }

        }

        private void Main_MouseClick(object sender, MouseEventArgs e)
        {
            hexesChanged = true;
            // Reset status bar
            statusText.Text = "Ready";
            statusText.ForeColor = SystemColors.ControlText;

            if (board != null && graphicsEngine != null)
            {
                // need to account for any offset
                Point mouseClick = new Point(e.X - graphicsEngine.BoardXOffset, e.Y - graphicsEngine.BoardYOffset);

                Hex clickedHex = board.FindHexMouseClick(mouseClick);

                if (clickedHex == null)
                {
                    board.BoardState.ActiveHex = null;
                    this.label_active.Text = "0";
                    label3.Enabled = false;
                    control_HexType.Enabled = false;
                }
                else
                {   
                    board.BoardState.ActiveHex = clickedHex;
                    Point hexLocation = board.FindHexOnBoard(clickedHex);
                    if (Hexagonal.Math.IsEven(hexLocation.Y))
                        hexLocation.X -= 1;
                    this.label_active.Text = hexLocation.Y + "," + hexLocation.X;

                    // Locked hexes
                    if (lastActiveLocked != null)
                    {
                        lastActiveLocked.HexState.Locked = true;
                        lastActiveLocked = null;
                    }
                    if (board.BoardState.ActiveHex.HexState.Locked)
                    {
                        board.BoardState.ActiveHex.HexState.BackgroundImage = (Bitmap)global::GeoSwarm_Editor.Properties.Resources.lockedHex_active;
                        lastActiveLocked = board.BoardState.ActiveHex;
                        label3.Enabled = false;
                        control_HexType.Enabled = false;
                        statusText.Text = "The selected hex is locked. It may not be changed.";
                        return;
                    }

                    if (!board.BoardState.ActiveHex.HexState.Locked && board.BoardState.ActiveHex.HexState.Outside)
                    {
                        statusText.Text = "The selected hex is outside the main area. It can only be set to a spawn or exit hex.";
                    }
                    
                    // Enable hex properties
                    label3.Enabled = true;
                    control_HexType.Enabled = true;

                    // Toggle between blocked hex and normal hex when a hex is right-clicked on
                    // For a list of hex types, see HexState.cs
                    try
                    {
                        if (e.Button == MouseButtons.Right && control_HexTool.SelectedIndex > 0)
                        {
                            if (clickedHex.HexState.HexType == 0)
                                clickedHex.HexState.HexType = control_HexTool.SelectedIndex;
                            else
                            {
                                // If it's no longer an exit hex, get rid of it
                                if (clickedHex.HexState.HexType == 6)
                                {
                                    if (RemoveExitHex(clickedHex))
                                    {
                                        clickedHex.HexState.BackgroundColor = board.HexBackgroundColor;
                                        clickedHex.HexState.HexType = 0;
                                    }
                                }

                                // If it's no longer a spawn hex, get rid of it
                                else if (clickedHex.HexState.HexType == 5)
                                {
                                    if (RemoveSpawnHex(clickedHex))
                                    {
                                        clickedHex.HexState.BackgroundColor = board.HexBackgroundColor;
                                        clickedHex.HexState.HexType = 0;
                                    }
                                }

                                else
                                {
                                    clickedHex.HexState.BackgroundColor = board.HexBackgroundColor;
                                    clickedHex.HexState.HexType = 0;
                                }
                            }
                        }
                    }
                    catch (Exception) { }
                    // Load the hex properties again
                    LoadHexProperties(clickedHex);
                }

            }
        }

        /*
         * Hover effect when the mouse moves over hexes
         */
        private void Main_MouseMove(object sender, MouseEventArgs e)
        {
            //mousepos.Text = e.X.ToString() + "," + e.Y.ToString();
            if (board != null && graphicsEngine != null)
            {
                Point mouseHover = new Point(e.X - graphicsEngine.BoardXOffset, e.Y - graphicsEngine.BoardYOffset);

                Hex hoveredHex = board.FindHexMouseClick(mouseHover);

                if (hoveredHex == null)
                {
                    board.BoardState.HoveredHex = null;
                    this.label_hovered.Text = "0";
                }
                else if (hoveredHex.Equals(board.BoardState.ActiveHex))
                {
                    board.BoardState.HoveredHex = null;
                    this.label_hovered.Text = this.label_active.Text;
                }
                else
                {
                    hexesChanged = true;
                    board.BoardState.HoveredHex = hoveredHex;
                    Point hexLocation = board.FindHexOnBoard(hoveredHex);
                    if (Hexagonal.Math.IsEven(hexLocation.Y))
                        hexLocation.X -= 1;
                    this.label_hovered.Text = (hexLocation.Y) + "," + (hexLocation.X);
                    //hoveredHex.HexState.BorderColor = Color.FromArgb(0, 157, 6);
                    hoveredHex.HexState.BorderColor = Color.White;
                }

            }
        }

        private void Main_Load(object sender, EventArgs e)
        {
            control_HexType.SelectedIndex = 0;
        }

        private void control_HexType_DropDownClosed(object sender, EventArgs e)
        {
            try
            {
                // If it's a spawn hex, add it
                if (control_HexType.SelectedIndex == 5)
                    AddSpawnHex(board.BoardState.ActiveHex);

                // If it's an exit hex, add it
                if (control_HexType.SelectedIndex == 6)
                    AddExitHex(board.BoardState.ActiveHex);

                // If it was an exit hex or a spawn hex
                if (board.BoardState.ActiveHex.HexState.HexType == 5 || board.BoardState.ActiveHex.HexState.HexType == 6)
                {
                    // If it's no longer an exit hex, get rid of it
                    if (board.BoardState.ActiveHex.HexState.HexType == 6)
                    {
                        if (RemoveExitHex(board.BoardState.ActiveHex))
                            board.BoardState.ActiveHex.HexState.HexType = control_HexType.SelectedIndex;
                        LoadHexProperties(board.BoardState.ActiveHex);
                    }

                    // If it's no longer a spawn hex, get rid of it
                    else if (board.BoardState.ActiveHex.HexState.HexType == 5)
                    {
                        if (RemoveSpawnHex(board.BoardState.ActiveHex))
                            board.BoardState.ActiveHex.HexState.HexType = control_HexType.SelectedIndex;
                        LoadHexProperties(board.BoardState.ActiveHex);
                    }
                }

                // If it wasn't an exit hex or a spawn hex
                else
                {
                    board.BoardState.ActiveHex.HexState.HexType = control_HexType.SelectedIndex;
                    LoadHexProperties(board.BoardState.ActiveHex);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message,"Exception Handler");
                statusText.Text = ex.Message;
                statusText.ForeColor = Color.Red;
                LoadHexProperties(board.BoardState.ActiveHex);
            }
        }

        /*
         * Loads properties of the hex into the editor
         */
        private void LoadHexProperties(Hex hex)
        {
            // If it's a spawn hex
            if (hex.HexState.HexType == 5)
            {
                groupBox2.Enabled = true;
                label5.Enabled = true;
                control_ExitHex.Enabled = true;
                control_HexID.Text = hex.HexState.HexID.ToString();
                if(control_ExitHex.Items.Count > 0)
                    control_ExitHex.SelectedIndex = control_ExitHex.Items.IndexOf(hex.HexState.ExitHex);
            }
            // If it's an exit hex
            else if (hex.HexState.HexType == 6)
            {
                groupBox2.Enabled = true;
                label5.Enabled = false;
                control_ExitHex.Enabled = false;
                control_HexID.Text = hex.HexState.HexID.ToString();
            }
            // If it isn't a spawn or an exit hex
            else
            {
                groupBox2.Enabled = false;
            }
            control_HexType.SelectedIndex = hex.HexState.HexType;
        }

        /*
         * Adds an exit hex
         */
        private void AddExitHex(Hex hex)
        {
            hex.HexState.HexID = ++this.lastHexID;
            control_ExitHex.Items.Add(hex);
        }

        /*
         * Removes an exit hex
         */
        private bool RemoveExitHex(Hex hex)
        {
            try
            {
                if (control_ExitHex.Items.Count == 1)
                    throw new Exception("You must have at least one exit hex.");
                control_ExitHex.Items.RemoveAt(control_ExitHex.Items.IndexOf(hex));
                hex.HexState.HexID = 0;
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Exception Handler");
                statusText.Text = ex.Message;
                statusText.ForeColor = Color.Red;
                return false;
            }
        }

        /*
         * Adds a spawn hex
         */
        private void AddSpawnHex(Hex hex)
        {
            if (control_ExitHex.Items.Count < 1)
                throw new Exception("You need to have at least one exit hex.");
            hex.HexState.HexID = ++this.lastHexID;
            hex.HexState.ExitHex = (Hex)control_ExitHex.Items[0];
            spawnHexes.Add(hex);
            hexesChanged = true;
        }

        /*
         * Removes a spawn hex
         */
        private bool RemoveSpawnHex(Hex hex)
        {
            try
            {
                if (spawnHexes.Count == 1)
                    throw new Exception("You must have at least one spawn hex.");
                spawnHexes.RemoveAt(spawnHexes.IndexOf(hex));
                hex.HexState.HexID = 0;
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Exception Handler");
                statusText.Text = ex.Message;
                statusText.ForeColor = Color.Red;
                return false;
            }
        }

        private void control_ExitHex_DropDownClosed(object sender, EventArgs e)
        {
            board.BoardState.ActiveHex.HexState.ExitHex = (Hex)control_ExitHex.SelectedItem;
        }

        private void levelDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            levelData.Show();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            about.Show();
        }

        private void button_LevelData_Click(object sender, EventArgs e)
        {
            levelData.Show();
        }

        private void refreshTimer_Tick(object sender, EventArgs e)
        {
            //Force the next Paint() if the grid has changed
            if (hexesChanged)
            {
                hexesChanged = false;
                this.Invalidate();
            }
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveDialog.ShowDialog();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveFilename == string.Empty)
                SaveDialog.ShowDialog();
            else
                SaveLevel(saveFilename);
        }

        private void SaveDialog_FileOk(object sender, CancelEventArgs e)
        {
            try
            {
                string filename = SaveDialog.FileName;
                saveFilename = filename;
                SaveLevel(filename);
            }
            catch (Exception ex)
            {
                MessageBox.Show("An Error Occured: " + ex.Message, "Exception Handler");
            }
        }

        private void SaveLevel(string filename)
        {
            this.Text = defaultTitle + " - " + levelData.LevelName + " - " + filename;

            GameLevel gameLevel = new GameLevel(board);
            gameLevel.LevelName = levelData.LevelName;
            gameLevel.LevelDescrption = levelData.LevelDesc;
            gameLevel.LevelHint1 = levelData.LevelHint1;
            gameLevel.LevelHint2 = levelData.LevelHint2;
            gameLevel.StartingCash = levelData.StartCash;
            gameLevel.StartingLives = levelData.StartLives;
            gameLevel.IsEndless = levelData.EndlessWaves;
            gameLevel.Creeps = levelData.EnabledCreeps;
            gameLevel.CreepData = levelData.CreepData;
            gameLevel.Towers = levelData.EnabledTowers;
            gameLevel.TowerData = levelData.TowerData;
            gameLevel.Waves = levelData.Waves;
            gameLevel.WaveDelay = levelData.WaveDelay;
            gameLevel.SpawnDelay = levelData.SpawnDelay;
            gameLevel.SpeedFactor = levelData.SpeedFactor;
            gameLevel.HealthFactor = levelData.HealthFactor;
            gameLevel.WealthFactor = levelData.WealthFactor;

            // Exit hexes
            Hex[] exits = new Hex[control_ExitHex.Items.Count];
            control_ExitHex.Items.CopyTo(exits, 0);
            gameLevel.Exits = exits;

            // Spawn hexes
            Hex[] spawns = new Hex[spawnHexes.Count];
            spawnHexes.CopyTo(spawns, 0);
            gameLevel.Spawns = spawns;

            gameLevel.BuildLevel(filename);
        }

        public void SaveLevelProg(string filename)
        {
            GameLevel gameLevel = new GameLevel(board);
            gameLevel.LevelName = levelData.LevelName;
            gameLevel.LevelDescrption = levelData.LevelDesc;
            gameLevel.LevelHint1 = levelData.LevelHint1;
            gameLevel.LevelHint2 = levelData.LevelHint2;
            gameLevel.StartingCash = levelData.StartCash;
            gameLevel.StartingLives = levelData.StartLives;
            gameLevel.IsEndless = levelData.EndlessWaves;
            gameLevel.Creeps = levelData.EnabledCreeps;
            gameLevel.CreepData = levelData.CreepData;
            gameLevel.Towers = levelData.EnabledTowers;
            gameLevel.TowerData = levelData.TowerData;
            gameLevel.Waves = levelData.Waves;
            gameLevel.WaveDelay = levelData.WaveDelay;
            gameLevel.SpawnDelay = levelData.SpawnDelay;
            gameLevel.SpeedFactor = levelData.SpeedFactor;
            gameLevel.HealthFactor = levelData.HealthFactor;
            gameLevel.WealthFactor = levelData.WealthFactor;

            // Exit hexes
            Hex[] exits = new Hex[control_ExitHex.Items.Count];
            control_ExitHex.Items.CopyTo(exits, 0);
            gameLevel.Exits = exits;

            // Spawn hexes
            Hex[] spawns = new Hex[spawnHexes.Count];
            spawnHexes.CopyTo(spawns, 0);
            gameLevel.Spawns = spawns;

            gameLevel.BuildLevel(filename);
        }

        private void hexKeyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new HexKey();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            new HexKey();
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ResetProgram();
        }

        private void ResetProgram()
        {
            if (MessageBox.Show("Do you want to save changes to \"" + levelData.LevelName + "\"?", "Save Changes", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                DialogResult saveResult = DialogResult.None;
                if (saveFilename == string.Empty)
                    saveResult = SaveDialog.ShowDialog();
                else
                    SaveLevel(saveFilename);
                if (saveResult == DialogResult.Cancel)
                    return;
            }
            restarting = false;
            saveFilename = string.Empty;
            lastHexID = 0;
            refreshTimer = new System.Windows.Forms.Timer(this.components);
            this.refreshTimer.Interval = 60;
            this.refreshTimer.Tick += new System.EventHandler(this.refreshTimer_Tick);
            control_HexType.SelectedIndex = 0;
            control_ExitHex.Items.Clear();
            control_HexID.Text = "0";
            label_active.Text = "0";
            label_hovered.Text = "0";
            groupBox2.Enabled = false;
            label3.Enabled = false;
            control_HexType.Enabled = false;
            CreateHexGrid();
            statusText.Text = "Ready";
            statusText.ForeColor = SystemColors.ControlText;
            levelData = new LevelData();
            spawnHexes = levelData.SpawnHexes;
            control_HexTool.SelectedIndex = 1;
            refreshTimer.Enabled = true;
            this.Text = defaultTitle;
            this.hexesChanged = true;
        }

        private void transferToDeviceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new LevelTransfer(this);
        }

        private void Main_FormClosed(object sender, FormClosedEventArgs e)
        {
            Environment.Exit(0);
        }

        private void control_HexType_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.hexesChanged = true;
        }

        private void OpenDialog_FileOk(object sender, CancelEventArgs e)
        {
            try
            {
                string filename = OpenDialog.FileName;
                saveFilename = filename;
                LoadLevel(filename);
            }
            catch (Exception ex)
            {
                MessageBox.Show("A problem occurred while opening this level: " + ex.Message, "Exception Handler");
            }
        }

        private void LoadLevel(string filename)
        {
            ResetProgram();

            GameLevel gameLevel = new GameLevel(board);
            gameLevel.OpenFile = filename;
            gameLevel.OpenLevel();

            levelData.LevelName = gameLevel.LevelName;
            levelData.LevelDesc = gameLevel.LevelDescrption;
            levelData.LevelHint1 = gameLevel.LevelHint1;
            levelData.LevelHint2 = gameLevel.LevelHint2;
            levelData.StartCash = gameLevel.StartingCash;
            levelData.StartLives = gameLevel.StartingLives;

            try
            {
                control_ExitHex.Items.AddRange(gameLevel.Exits);
                spawnHexes.AddRange(gameLevel.Spawns);
            }
            catch (ArgumentNullException) { }

            levelData.SpeedFactor = gameLevel.SpeedFactor;
            levelData.WealthFactor = gameLevel.WealthFactor;
            levelData.HealthFactor = gameLevel.HealthFactor;

            levelData.EnabledCreeps = gameLevel.Creeps;
            levelData.CreepData = gameLevel.CreepData;

            levelData.EnabledTowers = gameLevel.Towers;
            levelData.TowerData = gameLevel.TowerData;

            levelData.WaveDelay = gameLevel.WaveDelay;
            levelData.SpawnDelay = gameLevel.SpawnDelay;
            levelData.EndlessWaves = gameLevel.IsEndless;

            levelData.Waves = gameLevel.Waves;

            this.Text = defaultTitle + " - " + levelData.LevelName + " - " + filename;
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenDialog.ShowDialog();
        }
    }
}